package com.ruoyi.business.service;

import com.ruoyi.business.config.BingHuConfig;
import com.ruoyi.business.domain.dto.DeviceDTO;
import com.ruoyi.business.domain.response.BinghuCallMircoResponse;
import com.ruoyi.business.domain.response.BinghuDeviceListResponse;
import com.ruoyi.business.domain.response.BinghuTokenResponse;
import com.ruoyi.business.rpc.RemoteBinghuRpc;
import com.ruoyi.business.rpc.RemoteUserRpc;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * Feign服务调用封装类
 * 
 * 功能说明：
 * 1. 封装远程服务调用逻辑
 * 2. 提供统一的异常处理和重试机制
 * 3. 管理令牌的获取和刷新
 * 4. 简化API调用流程
 */
@Service
@Slf4j
public class FeignService {

    // 用户服务客户端
    @Resource
    private RemoteUserRpc userRpc;
    
    // 冰狐服务客户端
    @Resource
    private RemoteBinghuRpc binghuRpc;

    // 冰狐配置
    @Resource
    private BingHuConfig bingHuConfig;
    
    // 缓存服务
    @Resource
    private RedisCache redisCache;
    
    // 冰狐平台用户标识
    private static final String BINGHU_OPEN_ID = "A658247B12DB4E5326341B1FAED2BC18";
    
    // 令牌缓存键
    private static final String TOKEN_CACHE_KEY = Constants.BINGHU_TOKEN;

    /**
     * 获取设备列表，可根据在线状态过滤
     *
     * @param onlineState 在线状态，可选参数
     * @return 设备列表
     */
    public List<DeviceDTO> getDeviceList(String onlineState) {
        // 步骤1: 获取完整设备列表
        List<DeviceDTO> deviceList = getDeviceList();
        
        // 步骤2: 如果未指定在线状态，返回全部设备
        if (StringUtils.isEmpty(onlineState)) {
            return deviceList;
        }
        
        // 步骤3: 根据在线状态过滤设备
        log.info("按在线状态[{}]过滤设备列表", onlineState);
        return deviceList.stream()
                .filter(device -> onlineState.equals(device.getOnlineState().toString()))
                .collect(Collectors.toList());
    }

    /**
     * 获取所有设备列表
     *
     * @return 设备列表
     */
    public List<DeviceDTO> getDeviceList() {
        log.info("开始获取冰狐设备列表");
        
        // 步骤1: 准备调用参数
        String clientKey = bingHuConfig.getClientKey();
        String accessToken = getBinghuToken(false);
        
        // 步骤2: 执行远程调用，包含自动重试逻辑
        BinghuDeviceListResponse response = executeWithRetry(
            // 首次调用
            () -> binghuRpc.getDeviceList(clientKey, accessToken, BINGHU_OPEN_ID),
            // 重试调用（使用刷新的令牌）
            () -> {
                String refreshedToken = getBinghuToken(true);
                return binghuRpc.getDeviceList(clientKey, refreshedToken, BINGHU_OPEN_ID);
            },
            // 操作描述
            "获取设备列表"
        );
        
        // 步骤3: 返回设备列表
        return response.getData().getList();
    }

    /**
     * 获取冰狐平台的访问令牌
     *
     * @param forceRefresh 是否强制刷新令牌
     * @return 有效的访问令牌
     */
    public String getBinghuToken(boolean forceRefresh) {
        // 步骤1: 检查是否需要强制刷新令牌
        if (forceRefresh) {
            log.info("强制刷新冰狐访问令牌");
            return fetchNewToken();
        }
        
        // 步骤2: 尝试从缓存获取令牌
        String cachedToken = (String) redisCache.getCacheObject(TOKEN_CACHE_KEY);
        
        // 步骤3: 如果缓存中没有令牌，获取新令牌
        if (StringUtils.isEmpty(cachedToken)) {
            log.info("缓存中无有效令牌，获取新令牌");
            return fetchNewToken();
        }
        
        // 步骤4: 返回缓存中的令牌
        log.info("使用缓存中的令牌");
        return cachedToken;
    }

    /**
     * 从冰狐平台获取新的访问令牌
     *
     * @return 新的访问令牌
     */
    public String fetchNewToken() {
        log.info("开始从冰狐平台获取新令牌");
        
        try {
            // 步骤1: 准备调用参数
            String clientKey = bingHuConfig.getClientKey();
            String clientSecret = bingHuConfig.getClientSecret();
            
            // 步骤2: 调用获取令牌接口
            BinghuTokenResponse response = binghuRpc.getToken(clientKey, clientSecret);
            
            // 步骤3: 验证响应状态
            validateResponse(response, "获取令牌");
            
            // 步骤4: 提取令牌信息
            String newToken = response.getData().getAccessToken();
            int expiresIn = response.getData().getExpiresIn();
            
            // 步骤5: 缓存令牌（设置过期时间比实际少10秒，确保在令牌过期前刷新）
            int cacheExpireTime = Math.max(expiresIn - 10, 1);
            redisCache.setCacheObject(TOKEN_CACHE_KEY, newToken, cacheExpireTime, TimeUnit.SECONDS);
            log.info("成功获取新令牌，有效期{}秒", expiresIn);
            
            return newToken;
        } catch (Exception e) {
            log.error("获取冰狐令牌异常", e);
            throw new ServiceException("获取访问令牌失败: " + e.getMessage());
        }
    }

    /**
     * 调用冰狐微服务
     *
     * @param serviceName 微服务名称
     * @param isDev       是否开发环境
     * @param param       调用参数
     * @return 调用结果
     */
    public String callMicroService(String serviceName, Boolean isDev, String param) {
        log.info("开始调用冰狐微服务: {}, 开发模式: {}", serviceName, isDev);
        
        // 步骤1: 准备调用参数
        String clientKey = bingHuConfig.getClientKey();
        String accessToken = getBinghuToken(false);
        
        // 步骤2: 执行远程调用，包含自动重试逻辑
        BinghuCallMircoResponse response = executeWithRetry(
            // 首次调用
            () -> binghuRpc.callMicroService(clientKey, accessToken, serviceName, isDev, param),
            // 重试调用（使用刷新的令牌）
            () -> {
                String refreshedToken = getBinghuToken(true);
                return binghuRpc.callMicroService(clientKey, refreshedToken, serviceName, isDev, param);
            },
            // 操作描述
            "调用微服务[" + serviceName + "]"
        );
        
        // 步骤3: 返回调用结果
        return response.getData().getName();
    }

    /**
     * 获取系统用户信息
     *
     * @return 用户信息
     */
    public SysUser getSysUserByRpc() {
        log.info("开始获取系统用户信息");
        
        try {
            // 步骤1: 调用用户服务
            R<SysUser> response = userRpc.getSysUserByRpc();
            
            // 步骤2: 验证响应状态
            if (StringUtils.isNull(response) || response.getCode() != 200) {
                String errorMsg = response != null ? response.getMsg() : "响应为空";
                log.error("获取用户信息失败: {}", errorMsg);
                throw new ServiceException("获取用户信息失败: " + errorMsg);
            }
            
            // 步骤3: 验证响应数据
            SysUser user = response.getData();
            if (StringUtils.isNull(user)) {
                log.error("获取用户信息失败: 响应数据为空");
                throw new ServiceException("获取用户信息失败: 未找到用户数据");
            }
            
            // 步骤4: 返回用户信息
            return user;
        } catch (Exception e) {
            log.error("获取用户信息异常", e);
            throw new ServiceException("获取用户信息失败: " + e.getMessage());
        }
    }
    
    /**
     * 执行带有自动重试的远程调用
     * 
     * @param <T> 响应类型
     * @param firstCall 首次调用函数
     * @param retryCall 重试调用函数
     * @param operationName 操作名称（用于日志）
     * @return 调用结果
     */
    private <T> T executeWithRetry(
            Supplier<T> firstCall, 
            Supplier<T> retryCall, 
            String operationName) {
        
        try {
            // 步骤1: 执行首次调用
            log.info("执行{}操作", operationName);
            T response = firstCall.get();
            
            // 步骤2: 验证首次调用结果
            if (isValidResponse(response)) {
                return response;
            }
            
            // 步骤3: 首次调用失败，执行重试
            log.info("{}首次调用失败，准备重试", operationName);
            response = retryCall.get();
            
            // 步骤4: 验证重试调用结果
            validateResponse(response, operationName);
            
            return response;
        } catch (Exception e) {
            log.error("{}操作异常", operationName, e);
            throw new ServiceException(operationName + "失败: " + e.getMessage());
        }
    }
    
    /**
     * 检查响应是否有效
     * 
     * @param response 响应对象
     * @return 是否有效
     */
    private boolean isValidResponse(Object response) {
        if (response == null) {
            return false;
        }
        
        if (response instanceof BinghuTokenResponse) {
            return ((BinghuTokenResponse) response).getState() == 1;
        } else if (response instanceof BinghuDeviceListResponse) {
            return ((BinghuDeviceListResponse) response).getState() == 1;
        } else if (response instanceof BinghuCallMircoResponse) {
            return ((BinghuCallMircoResponse) response).getState() == 1;
        }
        
        return false;
    }
    
    /**
     * 验证响应，无效则抛出异常
     * 
     * @param response 响应对象
     * @param operationName 操作名称
     */
    private void validateResponse(Object response, String operationName) {
        if (!isValidResponse(response)) {
            String errorMsg;
            if (response == null) {
                errorMsg = "响应为空";
            } else if (response instanceof BinghuTokenResponse) {
                errorMsg = "状态码: " + ((BinghuTokenResponse) response).getState();
            } else if (response instanceof BinghuDeviceListResponse) {
                errorMsg = "状态码: " + ((BinghuDeviceListResponse) response).getState();
            } else if (response instanceof BinghuCallMircoResponse) {
                errorMsg = "状态码: " + ((BinghuCallMircoResponse) response).getState();
            } else {
                errorMsg = "未知响应类型";
            }
            
            log.error("{}失败: {}", operationName, errorMsg);
            throw new ServiceException(operationName + "失败");
        }
    }
}
